/* 
 * File:   ParStack.h
 * Author: tomas
 *
 * Created on May 3, 2009, 5:09 PM
 */

#ifndef _PARSTACK_H
#define	_PARSTACK_H

#include <stack>
#include <cmath>

#include "stackrecord.h"
#include "Instance.h"

#include <boost/serialization/vector.hpp>
#include <boost/serialization/access.hpp>


struct AvailableWork {
    unsigned recCount; // pocet zaznamu, ktere lze poslat dale

//    // toto se ted nepouziva!!
//    unsigned workWeight; // "velikost" prace pro odeslani
//
//    AvailableWork(size_t _recCount, size_t _workWeight) : recCount(_recCount), workWeight(_workWeight) {
//    }

    AvailableWork(size_t _recCount) : recCount(_recCount) {
    }

};


// musel jsem vytvorit takovou to tridu kuli serializaci vektoru StackRecordu
// nevim jestli to nejde udelat nejak lip, ja sem na to neprisel
class WorkUnit  {
public:
    std::vector<StackRecord> u;

    typedef std::vector<StackRecord>::iterator iterator;
    typedef std::vector<StackRecord>::const_iterator const_iterator;

private:
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & this->u;
    }
};


class ParStack : public std::stack<StackRecord> {
private:

    typedef std::stack<StackRecord> Base;

    typedef std::deque<StackRecord> DeQue;

    Instance* pInstance;

public:
    //typedef std::vector<StackRecord> workUnit_t;

    ParStack(Instance* i) : pInstance(i) {
    }

    static void vypisWorkItems(WorkUnit & workItem) {


        for(WorkUnit::iterator i = workItem.u.begin(); i != workItem.u.end(); ++i) {
            std::cout << *i << std::endl;
        }

    }

    void dump() {
        for(DeQue::iterator it=c.begin(); it != c.end(); ++it) {
            std::cout << *it << std::endl;
        }
    }

    int min(int prvni, int druhy) {

        if( prvni <= druhy)
            return prvni;
        else
            return druhy;

    }

    // minimalni pocet zaznamu ze zasobniku ktere stoji za to poslat
    static const unsigned min_rec_count = 4;

    // reqCount - pocet zadatelu o praci
    // pokud neni dostatek prace na rozdeleni, vraci vector o velikosti 0
    std::vector<WorkUnit> divide(size_t reqCount) {
        size_t maxDepth = pInstance->q() - 3;

        AvailableWork w = getAvailableWork(maxDepth);

        // kolik kosiku sem schopen maximalne naplnit (vcetne toho, co si necham)
        unsigned max_pocet_kosiku = w.recCount / min_rec_count;

        if(max_pocet_kosiku == 0) {
            std::vector<WorkUnit> workUnits(0);
            return workUnits;
        }

        // max_pocet_kosiku - 1 - musim pocitat i se sebou, nejake stavy mi musi zbyt
        unsigned pocet_kosiku = min(max_pocet_kosiku - 1, reqCount);

        std::vector<WorkUnit> workUnits(pocet_kosiku);
        
        if(pocet_kosiku == 0) {
            // nemam co rozdavat
            return workUnits;
        }

        unsigned actIndex = 0;
        // "rozdavam stavy ze zasobniku jako karty zadatelum"
        for(DeQue::iterator it = c.begin(); it != c.end(); ) {
            if (it->depth <= maxDepth) {
                if (actIndex == workUnits.size()) {
                    // tento stav mi zustane
                    ++it;
                    actIndex = 0;
                } else {
                    // tento stav preposlu
                    workUnits[actIndex++].u.push_back(*it);
//                    std::cout << *it << std::endl;
                    // kdyz smazu objekt nad iteratorem, iterator se zneplatni... erase mi vrati platny iterator na dalsi prvek
                    it = c.erase(it);
                }
            } else {
                ++it;
            }
        }
        return workUnits;
    }

    void addWorkUnits(const WorkUnit & work) {
        if (!work.u.empty()) {
            for(WorkUnit::const_iterator it = work.u.begin(); it != work.u.end(); ++it) {
                c.push_back(*it);
            }
        }
    }

private:

    AvailableWork getAvailableWork(size_t maxDepth) {
        unsigned recCount = 0;
//        unsigned workWeight = 0;

        for (DeQue::iterator i = c.begin(); i != c.end(); ++i) {

            if (i->depth <= maxDepth) {
                //                workWeight += computeWeight(pInstance->q, i->depth);
                recCount++;
            }

        }
        return AvailableWork(recCount);
    }

    //    unsigned computeWeight(unsigned q, unsigned depth) {
    //
    //        return pow( q - depth, 8 );
    //
    //    }
};



#endif	/* _PARSTACK_H */



